home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Utilities / Printer / djf_for_3.0 / printpage.c < prev    next >
C/C++ Source or Header  |  1992-10-18  |  19KB  |  813 lines

  1. /*
  2.  * Beginn der Deskjet 500 Codierung;
  3.  * Hier erstmal alle statischen Variablen zur Compression
  4.  * werden nur von PrintPage() und PrintScanLine() benutzt
  5.  */
  6.  
  7. #include    <limits.h>
  8. #include    <stdio.h>
  9. #ifdef    __ARCHITECTURE__
  10. #include    <mach/cthreads.h>
  11. #else
  12. #include    <cthreads.h>
  13. #endif
  14. #include    <windowserver/printmessage.h>
  15.  
  16. #define    MIN(a,b)    ((a)<(b)?(a):(b))
  17. #define    sendsequence(s)    (void) fwrite(s, 1, sizeof(s), stdout)
  18.  
  19. #define zerostrip    1
  20.  
  21. static    int        in_mode;        /* current mode */
  22. static    unsigned char    *seed_row;
  23. static    unsigned char    obuf1[1024], obuf2[1024];
  24. static    unsigned char    *testrow = obuf1, *bestrow = obuf2;
  25. static    int        testsize, bestsize;
  26. static  char page_init[] = { 27, '*', 'p', '0', 'x', 'p', '1', 'Y', 27, '*', 'r', '1', 'A' }; /* <e>*p0x1Y<e>*r1A gives yet another 1/8" 'headroom' */
  27. static  char page_exit[] = { 27, '*', 'r', 'B', 27, '&', 'l', '0', 'H' };
  28.  
  29. /*
  30.  *  Compression Routines taken from pclcomp by  Tony Parkhurst  
  31.  *  Email address:  tony@sdd.hp.com    -or-   hp-sdd!tony
  32.  */
  33. /*  Compression for mode 9 taken from ghostscript 2.5.2    */
  34.  
  35. /*
  36. **  Compress_0() does mode 0 compression (which is a no compression).
  37. ** always usable !!
  38. */
  39.  
  40. static int
  41. Compress_0(src, dest, count)
  42. unsigned char *src, *dest;
  43. int count;
  44. {
  45.     memcpy(dest, src, count);
  46.  
  47.     if ( zerostrip )
  48.         while ( count && dest[count-1] == 0 )
  49.             count--;
  50.  
  51.     return(count);
  52.  
  53. }
  54.  
  55.  
  56.  
  57. #ifdef    USE_1
  58.  
  59. /*
  60. ******************************************************************************
  61. **
  62. **       Compress_1() does PCL compression mode 1 on the data. 
  63. **       This mode is run length encoding.
  64. **
  65. **       Given an input byte count of n, then the worst case output size
  66. **       would be  2 * n.
  67. **
  68. ******************************************************************************
  69. */
  70.  
  71. static int
  72. Compress_1(src, dest, count)
  73. unsigned char *src, *dest;
  74. register int count;
  75. {
  76.     unsigned char *outptr = dest, *inptr = src;
  77.  
  78.     unsigned char data;        /* data values to match */
  79.  
  80.     unsigned char temp;
  81.  
  82.     int    repcount;        /* repeat count */
  83.  
  84.  
  85.     /*
  86.     **  "count" is the number of bytes in "src" to compress
  87.     **  into "dest".
  88.     */
  89.  
  90.     while ( count )
  91.     {
  92.         data = *inptr++;    /* get value to work with */
  93.         count--;
  94.  
  95.         repcount = 0;        /* no repeats yet */
  96.  
  97.         /*
  98.         **  Look for successive bytes that match "data".
  99.         */
  100.  
  101.         while ( count && *inptr == data )
  102.         {
  103.             repcount++;
  104.             inptr++;
  105.             count--;
  106.         }
  107.  
  108.         /*
  109.         **  Now if we are out of data (count == 0), then
  110.         **  if the repeated byte was zero, then we can ignore it
  111.         **  unless the user disabled zero stripping.
  112.         */
  113.  
  114.         if ( count == 0 && data == 0 && zerostrip )
  115.             break;                    /* done */
  116.  
  117.         /*
  118.         **  Now output the repeat count and the value.
  119.         **
  120.         **  If the repeat count exceeds 255, then we must send
  121.         **  more repeat-value pairs.
  122.         **
  123.         **  Each time thru the loop, repcount needs to be decremented
  124.         **  to keep in sync.  That is, if repcount == 256, then the
  125.         **  first time thru the loop, <255> <data> are output, then
  126.         **  repcount is now 1.  So the next time thru the loop, 
  127.         **  repcount needs to be adjusted to 0 so that the next pair
  128.         **  is <0> <data>.  Thus, 1 data plus 255 repeats of data
  129.         **  plus 1 data + 0 repeats of data ==> 257 total bytes
  130.         **  of "data", which is what a repcount of 256 means.
  131.         */ 
  132.  
  133.         do
  134.         {
  135.             temp = MIN(repcount, 255);
  136.  
  137.             *outptr++ = temp;
  138.  
  139.             repcount -= temp;
  140.  
  141.             *outptr++ = data;
  142.  
  143.         } while ( repcount-- );
  144.     }
  145.  
  146.     return ( outptr - dest );    /* size of compressed data */
  147. }
  148. #endif
  149.  
  150. #ifdef USE_2
  151. /*
  152. ******************************************************************************
  153. **
  154. **       Compress_2() does PCL compression mode 2 on the data. 
  155. **       This mode is a combination of modes 0 and 1.
  156. **
  157. **       Given an input byte count of n, then the worst case output size
  158. **       would be  n + (n + 127)/128.
  159. **
  160. ******************************************************************************
  161. */
  162.  
  163. static int
  164. Compress_2(src, dest, count)
  165. unsigned char *src, *dest;
  166. register int count;
  167. {
  168.     unsigned char    *outptr, *inptr;
  169.     unsigned char    *saveptr;
  170.  
  171.     unsigned char    data;            /* data byte */
  172.     unsigned char    lastbyte;        /* last byte */
  173.     int        repcount;        /* repeat count */
  174.     int        litcount;        /* literal count */
  175.  
  176.     /*
  177.     **  src points to the input data.
  178.     **  dest points to the output buffer.
  179.     **  count is the number of input bytes.
  180.     */
  181.  
  182.     inptr = src;
  183.     outptr = dest;
  184.  
  185.     /*
  186.     **  Start loop thru data.  Check for possible repeat at beginning.
  187.     */
  188.  
  189.     while ( count )
  190.     {
  191.         data = *inptr++;    /* get value to work with */
  192.         count--;
  193.  
  194.         repcount = 0;        /* no repeat count yet */
  195.  
  196.  
  197.         /* 
  198.         **  Check for repeat, since we are not in the middle
  199.         **  of a literal run, it does not have to be more than
  200.         **  two bytes of similar data.
  201.         */
  202.  
  203.         while ( count && *inptr == data )
  204.         {
  205.             repcount++;
  206.             inptr++;
  207.             count--;
  208.         }
  209.  
  210.         /*
  211.         **  Now, if we are out of data (count == 0), then
  212.         **  if the repeated byte was zero, then ignore it
  213.         **  completely (don't bother outputing the trailing zeros).
  214.         **
  215.         **  To always strip zero's, simply remove the "zerostrip"
  216.         **  from the test.
  217.         */
  218.  
  219.         if ( count == 0 && data == 0 && zerostrip)
  220.             break;            /* done */
  221.  
  222.  
  223.         /*
  224.         **  If there was a repeat (repcount > 0), then we
  225.         **  can output the command here, otherwise, we
  226.         **  need to go into literal run mode.
  227.         **
  228.         **  Note:  This is a while loop because the repeat count
  229.         **  may actually be greater than 127.
  230.         */
  231.  
  232.         if ( repcount >= 1 )        /* repeat mode */
  233.         {
  234.             while (repcount > 127)
  235.             {
  236.                 *outptr++ = 129;        /* count 127 */
  237.                 *outptr++ = data;        /* value */
  238.                 repcount-= 128;            /* offset */
  239.             }
  240.  
  241.             if (repcount > 0)
  242.             {
  243.                 *outptr++ = 256 - repcount;    /* count */
  244.                 *outptr++ = data;        /* value */
  245.  
  246.                 /*
  247.                 **  Now pop to the top of the loop 
  248.                 **  looking for more repeat counts.
  249.                 */
  250.  
  251.                 continue;            /* top of loop */
  252.             }
  253.  
  254.             /*
  255.             **  Special case:  If we have arrived at this point,
  256.             **  then repcount is now equal to 0.  This means
  257.             **  that when we entered this section, repcount
  258.             **  was a multiple of 128 (i.e. 128 :-).
  259.             **
  260.             **  This means that there were 129 identical bytes,
  261.             **  so the output does a replicate of 127 which
  262.             **  gives 128 bytes, and we now have one byte left
  263.             **  over which should NOT be output as a repeat
  264.             **  run, rather it should be merged into the following
  265.             **  literal run (if it exists).
  266.             **
  267.             **  So, we will simply fall thru to the next section
  268.             **  of code which assumes that we are working on 
  269.             **  a literal run.
  270.             */
  271.  
  272.         }
  273.  
  274.         /*
  275.         **  Literal run:  At this point, the current data byte
  276.         **  does NOT match the following byte.  We will transfer
  277.         **  these non-identical bytes until:
  278.         **
  279.         **       1)  we run out of input data (count == 0).
  280.         **       2)  we run out of room in this output block (128)
  281.         **       3)  we come across a value which occurs at least
  282.         **           three times in a row.  A value occuring only
  283.         **           twice in a row does NOT justify dropping
  284.         **           out of a literal run.
  285.         **
  286.         **  Special case:  If we run out of room in the output block
  287.         **  (which is 128 bytes), the last two values are the same,
  288.         **  AND there is more input, it makes sense to restart
  289.         **  the repeat detector in case the following bytes are
  290.         **  repeats of the two.  A simple check of the following
  291.         **  byte will determine this.
  292.         **  (This case falls out with the test for triples below).
  293.         **
  294.         **  Special case:  If we run out of room in the output block
  295.         **  (which is 128 bytes), the last value is the same as
  296.         **  the next one on the input, then it is better to let
  297.         **  that byte be used in a possible replicate run following
  298.         **  the literal run.  If the last byte matches ONLY the
  299.         **  following byte, (and not the one after that,) it is
  300.         **  a wash, but for best results, we will test the
  301.         **  following two bytes.
  302.         **
  303.         */
  304.  
  305.         litcount = 0;
  306.         saveptr = outptr++;    /* save location of the command byte */
  307.  
  308.         *outptr++ = data;    /* save the first byte. */
  309.  
  310.         lastbyte = data;    /* remember for testing */
  311.  
  312.         while ( count && litcount < 127 )
  313.         {
  314.             data = *inptr++;
  315.             count--;
  316.             litcount++;
  317.             *outptr++ = data;
  318.  
  319.             /*
  320.             **  Test to see if this byte matched the last one.
  321.             **  If so, check the next one for a triple.
  322.             */
  323.  
  324.             if ( lastbyte == data && count && *inptr == data )
  325.             {
  326.                 /*
  327.                 **  We have a triple, adjust accordingly.
  328.                 **
  329.                 **  Add two bytes back onto the input.
  330.                 */
  331.  
  332.                 count += 2;
  333.                 inptr -= 2;
  334.                 outptr -= 2;
  335.                 litcount -= 2;
  336.  
  337.                 break;        /* out of loop */
  338.             }
  339.  
  340.             lastbyte = data;    /* save data byte */
  341.         }
  342.  
  343.         /*
  344.         **  Check the special case number 2 above.
  345.         */
  346.  
  347.         if ( litcount == 127  &&  count > 1  &&  data == *inptr
  348.             &&  data == inptr[1] )
  349.         {
  350.             /*  Restore the last byte to the input stream */
  351.  
  352.             count += 1;
  353.             inptr -= 1;
  354.             outptr -= 1;
  355.             litcount -= 1;
  356.         }
  357.  
  358.  
  359.         /*
  360.         **  Save the literal run count.
  361.         */
  362.  
  363.         *saveptr = litcount;
  364.  
  365.         /*
  366.         **  Now go back to the top and look for repeats.
  367.         */
  368.     }
  369.  
  370.     count = outptr - dest;        /* for return value */
  371.  
  372.     return ( count );
  373. }
  374. #endif
  375.  
  376. #ifdef USE_3
  377. /*
  378. ******************************************************************************
  379. **
  380. **       Compress_3() does PCL compression mode 3 on the data. 
  381. **       This mode is delta row encoding.
  382. **
  383. **       Given an input byte count of n, then the worst case output size
  384. **       would be  n + (n + 7)/8
  385. **
  386. ******************************************************************************
  387. */
  388.  
  389. static int
  390. Compress_3(seed, new, dest, count)
  391. unsigned char *seed, *new, *dest;
  392. register int count;
  393. {
  394.     unsigned char *sptr=seed, *nptr=new, *dptr=dest;
  395.     unsigned char *saveptr;
  396.  
  397.     int    delta, xfer;
  398.     unsigned char    command;
  399.  
  400.  
  401.     /*
  402.     **  "count" is the number of bytes of data in "new" that need to
  403.     **  be compressed into "dest" using "seed" as the basis for diffs.
  404.     */
  405.  
  406.     while ( count > 0 )
  407.     {
  408.         delta = 0;        /* position counter */
  409.  
  410.         /*
  411.         **  Hunt through the data until the new data is different
  412.         **  from the seed data.
  413.         */
  414.  
  415.         while ( *sptr == *nptr && delta < count )
  416.         {
  417.             delta++;
  418.             sptr++;
  419.             nptr++;
  420.         }
  421.  
  422.         /*
  423.         **  Either a difference has been found, or we ran out of data.
  424.         */
  425.  
  426.         if ( delta >= count )    /* too far */
  427.             break;        /* done */
  428.  
  429.         count -= delta;
  430.         
  431.         /*
  432.         **  Now set up the output with the command byte[s].
  433.         **  (leaving the actual byte copy count till last.)
  434.         */
  435.  
  436.         /*
  437.         **  The first command byte can only hold up to 31.
  438.         **  If the delta is larger, then following bytes will
  439.         **  be needed.
  440.         */
  441.  
  442.         saveptr = dptr++;    /* save the address for command byte */
  443.  
  444.         command = MIN(delta, 31);
  445.  
  446.         /*
  447.         **  Any remaining count follows.
  448.         **
  449.         **  If count is 31, then a following byte must be given,
  450.         **  even if 0.  Same holds if 255 is given in succesive bytes.
  451.         */
  452.  
  453.         if ( command == 31 )
  454.         {
  455.             delta -= command;    /* adjust for first count */
  456.  
  457.             do {
  458.                 xfer = MIN(delta,255);
  459.  
  460.                 delta -= xfer;
  461.  
  462.                 *dptr++ = xfer;
  463.  
  464.             } while ( xfer == 255 );
  465.  
  466.  
  467.         }
  468.  
  469.  
  470.         /*
  471.         **  Now transfer up to 8 bytes, stopping when the new byte
  472.         **  matches the seed byte.  One could make a case for
  473.         **  transfering a matching byte too (if stuck in the middle
  474.         **  of the 8 bytes), but it does not impact the worst case,
  475.         **  and in the long run, the compression will not be as good.
  476.         **  Also, we need to make sure that we don't overrun count.
  477.         **  ("count" is checked first so we don't reference past the
  478.         **  end of the memory block).
  479.         */
  480.  
  481.         for ( xfer = 0; 
  482.             count && *sptr != *nptr && xfer < 8;
  483.                 xfer++)
  484.         {
  485.             *dptr++ = *nptr++;    /* transfer byte */
  486.             sptr++;            /* bump seed pointer too */
  487.             count--;
  488.         }
  489.  
  490.         /*
  491.         **  Now xfer is the number of bytes transfered, but the
  492.         **  number range is 3 bits (0-7), so decrement and merge
  493.         **  it into the command byte and put it in the data.
  494.         */
  495.  
  496.         command += ((xfer - 1) << 5);
  497.  
  498.         *saveptr = command;
  499.  
  500.     }
  501.  
  502.     return ( dptr - dest );
  503. }
  504. #endif
  505.  
  506. #ifdef USE_9
  507. /*
  508. ******************************************************************************
  509. **
  510. **       Compress_9() does PCL compression mode 9 on the data. 
  511. **       This mode is delta row encoding.
  512. **
  513. **       Given an input byte count of n, then the worst case output size
  514. **       would be  ???
  515. **
  516. ******************************************************************************
  517. */
  518.  
  519. static int
  520. Compress_9(previous, current, compressed, bytecount)
  521. unsigned const char *previous, *current;
  522. unsigned char *compressed;
  523. register int bytecount;
  524.  
  525. /*
  526.  * Mode 9 2D compression for the HP DeskJet 500C. This mode can give
  527.  * very good compression ratios, especially if there are areas of flat
  528.  * colour (or blank areas), and so is 'highly recommended' for colour
  529.  * printing in particular because of the very large amounts of data which
  530.  * can be generated
  531.  */
  532. {
  533.   register const unsigned char *cur = current;
  534.   register const unsigned char *prev = previous;
  535.   register unsigned char *out = compressed;
  536.   const unsigned char *end = current + bytecount;
  537.  
  538.   while (cur < end) {        /* Detect a run of unchanged bytes. */
  539.     const unsigned char *run = cur;
  540.     register const unsigned char *diff;
  541.     int offset;
  542.     while (cur < end && *cur == *prev) {
  543.       cur++, prev++;
  544.     }
  545.     if (cur == end)
  546.       break;            /* rest of row is unchanged */
  547.     /* Detect a run of changed bytes. */
  548.     /* We know that *cur != *prev. */
  549.     diff = cur;
  550.     do {
  551.       prev++;
  552.       cur++;
  553.     } while (cur < end && *cur != *prev);
  554.  
  555.     /* Now [run..diff) are unchanged, and */
  556.     /* [diff..cur) are changed. */
  557.     offset = diff - run;
  558.     {
  559.       const unsigned char *stop_test = cur - 4;
  560.       int dissimilar, similar;
  561.  
  562.       while (diff < cur) {
  563.     const unsigned char *compr = diff;
  564.     const unsigned char *next;    /* end of run */
  565.     unsigned char value=0;
  566.     while (diff <= stop_test &&
  567.            ((value = *diff) != diff[1] ||
  568.         value != diff[2] ||
  569.         value != diff[3]))
  570.       diff++;
  571.  
  572.     /* Find out how long the run is */
  573.     if (diff > stop_test)    /* no run */
  574.       next = diff = cur;
  575.     else {
  576.       next = diff + 4;
  577.       while (next < cur && *next == value)
  578.         next++;
  579.     }
  580.  
  581. #define MAXOFFSETU 15
  582. #define MAXCOUNTU 7
  583.     /* output 'dissimilar' bytes, uncompressed */
  584.     if ((dissimilar = diff - compr)) {
  585.       int temp, i;
  586.  
  587.       if ((temp = --dissimilar) > MAXCOUNTU)
  588.         temp = MAXCOUNTU;
  589.       if (offset < MAXOFFSETU)
  590.         *out++ = (offset << 3) | (unsigned char) temp;
  591.       else {
  592.         *out++ = (MAXOFFSETU << 3) | (unsigned char) temp;
  593.         offset -= MAXOFFSETU;
  594.         while (offset >= 255) {
  595.           *out++ = 255;
  596.           offset -= 255;
  597.         }
  598.         *out++ = offset;
  599.       }
  600.       if (temp == MAXCOUNTU) {
  601.         temp = dissimilar - MAXCOUNTU;
  602.         while (temp >= 255) {
  603.           *out++ = 255;
  604.           temp -= 255;
  605.         }
  606.         *out++ = (unsigned char) temp;
  607.       }
  608.       for (i = 0; i <= dissimilar; i++)
  609.         *out++ = *compr++;
  610.       offset = 0;
  611.     }            /* end uncompressed */
  612. #define MAXOFFSETC 3
  613. #define MAXCOUNTC 31
  614.     /* output 'similar' bytes, run-length encoded */
  615.     if ((similar = next - diff)) {
  616.       int temp;
  617.  
  618.       if ((temp = (similar -= 2)) > MAXCOUNTC)
  619.         temp = MAXCOUNTC;
  620.       if (offset < MAXOFFSETC)
  621.         *out++ = 0x80 | (offset << 5) | (unsigned char) temp;
  622.       else {
  623.         *out++ = 0x80 | (MAXOFFSETC << 5) | (unsigned char) temp;
  624.         offset -= MAXOFFSETC;
  625.         while (offset >= 255) {
  626.           *out++ = 255;
  627.           offset -= 255;
  628.         }
  629.         *out++ = offset;
  630.       }
  631.       if (temp == MAXCOUNTC) {
  632.         temp = similar - MAXCOUNTC;
  633.         while (temp >= 255) {
  634.           *out++ = 255;
  635.           temp -= 255;
  636.         }
  637.         *out++ = (unsigned char) temp;
  638.       }
  639.       *out++ = value;
  640.       offset = 0;
  641.     }            /* end compressed */
  642.     diff = next;
  643.       }
  644.     }
  645.   }
  646.   return out - compressed;
  647. }
  648.  
  649. #endif
  650.  
  651. /*
  652.  * print a scanline and select the modes
  653.  */ 
  654.  
  655. void PrintScanLine(unsigned char *buf, int anz)
  656. {
  657.     int        minmode = 0;
  658.     unsigned char    *temprow;
  659.     
  660.     /*    Anything is better than this    */
  661.     bestsize = INT_MAX;
  662.  
  663. #ifdef    USE_9
  664.     testsize = seed_row ? Compress_9(seed_row, buf, testrow, anz)
  665.                 : INT_MAX;
  666.     if (testsize + (in_mode != 9 ? 2 : 0) < bestsize) {
  667.         temprow = testrow;
  668.         testrow = bestrow;
  669.         bestrow = temprow;
  670.         bestsize = testsize;
  671.         minmode = 9;
  672.     }
  673. #endif
  674.  
  675. #ifdef    USE_3
  676.     testsize = seed_row ? Compress_3(seed_row, buf, testrow, anz)
  677.                 : INT_MAX;
  678.     if (testsize + (in_mode != 3 ? 2 : 0) < bestsize) {
  679.         temprow = testrow;
  680.         testrow = bestrow;
  681.         bestrow = temprow;
  682.         bestsize = testsize;
  683.         minmode = 3;
  684.     }
  685. #endif
  686.  
  687. #ifdef    USE_2
  688.     /*    mode 2 is always tested                    */
  689.     testsize = Compress_2( buf, testrow, anz);
  690.     if (testsize + (in_mode != 2 ? 2 : 0) < bestsize) {
  691.         temprow = testrow;
  692.         testrow = bestrow;
  693.         bestrow = temprow;
  694.         bestsize = testsize;
  695.         minmode = 2;
  696.     }
  697. #endif
  698.  
  699. #ifdef    USE_1
  700. /*    Usually don't check mode 1 (it is rarely better than mode 2) and */
  701. /*    mode 0 (mode 2 is only about 1% longer in worst case)         */
  702.  
  703.     testsize = Compress_1( buf, testrow, anz);
  704.     if (testsize + (in_mode != 1 ? 2 : 0) < bestsize) {
  705.         temprow = testrow;
  706.         testrow = bestrow;
  707.         bestrow = temprow;
  708.         bestsize = testsize;
  709.         minmode = 1;
  710.     }
  711. #endif
  712.  
  713. /* Mode 0 is cheap and always there */
  714.     testsize = Compress_0(buf, testrow, anz);
  715.     if (testsize + (in_mode != 0 ? 1 : 0) < bestsize) {
  716.         bestrow = buf;
  717.         bestsize = testsize;
  718.         minmode = 0;
  719.     }
  720.     if ( in_mode != minmode ) {
  721.         printf(minmode ? "%dm" : "m", minmode);
  722.         in_mode = minmode;
  723.     }
  724.     
  725.     /* <esc>*b has already been output */
  726.  
  727.     printf(bestsize ? "%dw" : "w", bestsize);
  728.  
  729.     if ( fwrite( bestrow, 1, bestsize, stdout) < bestsize ) {
  730.         /* check for error and exit */
  731.  
  732.         if ( ferror(stdout) )
  733.         {
  734.             perror("Output error");
  735.             exit(-2);
  736.         }
  737.     }
  738. }
  739.  
  740. /*
  741.  * small improvement for speed:
  742.  * if we know that we are printing (one of the pins 0-49 is active) it is
  743.  * faster to print a null line, than skipping it via *b%dY. This way the
  744.  * printing head doesn't need to print, move vertically and restart printing.
  745.  */
  746. #define    PINS    50
  747.  
  748. void PrintPage(void)
  749. {
  750.     /* output the whole Page to the printer */
  751.     register unsigned char *sp, *p;
  752.     register int i;
  753.     extern NXPrintPageMessage ppm;
  754.     extern mutex_t mutex1;
  755.     int in_seq = -1, empty_lines = 0;
  756.  
  757.     mutex_lock(mutex1);
  758.     sendsequence(page_init);    /* start plot at current position */
  759.     mutex_unlock(mutex1);
  760.  
  761.     in_mode = -1;            /* invalidate some vars */
  762.     seed_row = NULL;
  763.  
  764.     printf("\033*b");        /* prepare */
  765.  
  766.     for (i = ppm.pixelsHigh, sp = (unsigned char *) ppm.printerData;
  767.          i > 0; i--, sp += ppm.bytesPerRow) {
  768.  
  769. /*
  770. #ifdef DEBUG
  771.         fprintf(stderr, "Line: %d/%d\n", ppm.pixelsHigh - i, ppm.pixelsHigh);
  772. #endif
  773. */
  774.         /*    Im Druck einer 50er Zeile?    */
  775.         if (in_seq >= 0) {
  776.             mutex_lock(mutex1);
  777.             PrintScanLine(sp, ppm.bytesPerRow);
  778.             mutex_unlock(mutex1);
  779.             /*    Ende der ganzen Zeile?    */
  780.             if (++in_seq == PINS)
  781.                 in_seq = -1;
  782.         }
  783.         else {
  784.  
  785.             for (p = sp + ppm.bytesPerRow - 1; p >= sp && *p == 0; p--)
  786.                 ;
  787.                     
  788.             /*    Leere Zeile?    */
  789.             if (p < sp)
  790.                 empty_lines++;
  791.             else {
  792.                 /*    Volle Zeile nach leeren Zeilen    */
  793.                 if (empty_lines > 0) {
  794.                     (void) fprintf(stdout, "%dy", empty_lines);
  795.                 }
  796.                 mutex_lock(mutex1);
  797.                 PrintScanLine(sp, ppm.bytesPerRow);
  798.                 mutex_unlock(mutex1);
  799.                 empty_lines = 0;
  800.                 /*    Erster Pin    */
  801.                 in_seq = 0;
  802.             }
  803.         }
  804.         seed_row = sp;
  805.     }
  806.     
  807.     printf("M");            /* Must have one capital letter */
  808.  
  809.     mutex_lock(mutex1);
  810.     sendsequence(page_exit);    /* Eject page    */
  811.     mutex_unlock(mutex1);
  812. }
  813.